#include <atomic>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <string>
using namespace std;
#include <sys/socket.h>
#include <fcntl.h>
#include "root_device.h"
#include "cJSON.h"
#include "queue.h"

int set_property_byaddr(root_device *root, const cJSON *params, char *buf = NULL, int len = 0)
{
	cJSON *addr = cJSON_GetObjectItemCaseSensitive(params, "Addr");
	if (cJSON_IsString(addr) && addr->valuestring != NULL) {
		root->set_property_byaddr(addr->valuestring, params);
	}
	return 0;
}

int set_property_byarea(root_device *root, const cJSON *params, char *buf = NULL, int len = 0)
{
	cJSON *area = cJSON_GetObjectItemCaseSensitive(params, "Area");
	if (cJSON_IsString(area) && area->valuestring != NULL) {
		root->set_property_byarea(area->valuestring, params);
	}
	return 0;
}

int set_property_bysaio(root_device *root, const cJSON *params, char *buf = NULL, int len = 0)
{
	cJSON *area = cJSON_GetObjectItemCaseSensitive(params, "Scenario");
	if (cJSON_IsString(area) && area->valuestring != NULL) {
		root->set_property_bysaio(area->valuestring, params);
	}
	return 0;
}

int get_all_devstatus(root_device *root, const cJSON *params, char *buf, int len)
{
	root->get_all_devstatus(buf, len);
	send_msg(SERVICE_QUEUE_RSP, buf, strlen(buf));
	return 0;
}

int get_status_byaddr(root_device *root, const cJSON *params, char *buf, int len)
{
	cJSON *addr = cJSON_GetObjectItemCaseSensitive(params, "Addr");
	if (cJSON_IsString(addr) && addr->valuestring != NULL) {
		if (root->get_status_byaddr(addr->valuestring, buf, len) > 0) {
			send_msg(SERVICE_QUEUE_RSP, buf, strlen(buf));
		} else {
			snprintf(buf, sizeof(buf), "{}");
			send_msg(SERVICE_QUEUE_RSP, buf, strlen(buf));
		}
	}
	return 0;
}

int get_status_byarea(root_device *root, const cJSON *params, char *buf, int len)
{
	cJSON *area = cJSON_GetObjectItemCaseSensitive(params, "Area");
	if (cJSON_IsString(area) && area->valuestring != NULL) {
		root->get_status_byarea(area->valuestring, buf, len);
		send_msg(SERVICE_QUEUE_RSP, buf, strlen(buf));
	}
	return 0;
}

int get_status_bysaio(root_device *root, const cJSON *params, char *buf, int len)
{
	cJSON *saio = cJSON_GetObjectItemCaseSensitive(params, "Scenario");
	if (cJSON_IsString(saio) && saio->valuestring != NULL) {
		root->get_status_bysaio(saio->valuestring, buf, len);
		send_msg(SERVICE_QUEUE_RSP, buf, strlen(buf));
	}
	return 0;
}

int get_all_dev(root_device *root, const cJSON *params, char *buf, int len)
{
	root->get_all_dev(buf, len);
	send_msg(SERVICE_QUEUE_RSP, buf, strlen(buf));
	return 0;
}

int get_dev_byaddr(root_device *root, const cJSON *params, char *buf, int len)
{
	cJSON *addr = cJSON_GetObjectItemCaseSensitive(params, "Addr");
	if (root->get_dev_byaddr(addr->valuestring, buf, len) > 0) {
		send_msg(SERVICE_QUEUE_RSP, buf, strlen(buf));
	} else {
		snprintf(buf, sizeof(buf), "{}");
		send_msg(SERVICE_QUEUE_RSP, buf, strlen(buf));
	}
	return 0;
}

int get_dev_byarea(root_device *root, const cJSON *params, char *buf, int len)
{
	cJSON *area = cJSON_GetObjectItemCaseSensitive(params, "Area");
	if (cJSON_IsString(area) && area->valuestring != NULL) {
		root->get_dev_byarea(area->valuestring, buf, len);
		send_msg(SERVICE_QUEUE_RSP, buf, strlen(buf));
	}
	return 0;
}

int get_dev_bysaio(root_device *root, const cJSON *params, char *buf, int len)
{
	cJSON *saio = cJSON_GetObjectItemCaseSensitive(params, "Scenario");
	if (cJSON_IsString(saio) && saio->valuestring != NULL) {
		root->get_dev_bysaio(saio->valuestring, buf, len);
		send_msg(SERVICE_QUEUE_RSP, buf, strlen(buf));
	}
	return 0;
}

int cloud_online_event(root_device *root, const cJSON *params, char *buf, int len)
{
	cJSON *json = cJSON_CreateObject();
	cJSON_AddStringToObject(json, "Active", "SUBDEV_GET_TOPO");
	char *ptr = cJSON_Print(json);
	send_msg(CLOUD_QUEUE, ptr, strlen(ptr));
	free(ptr);
	cJSON_Delete(json);
	root->cloud_online_event();
	return 0;
}

int cloud_offline_event(root_device *root, const cJSON *params, char *buf, int len)
{
	root->cloud_offline_event();
	return 0;
}
/**********************************************************************************/
int gw_self_property_set(const cJSON *params, atomic_uint32_t &id)
{
	cJSON *json = cJSON_CreateObject();
	cJSON_AddStringToObject(json, "Active", "GW_SELF_PROPERTY_SET");
	cJSON *obj = cJSON_AddObjectToObject(json, "Params");
	cJSON_AddStringToObject(obj, "ID", to_string(++id).c_str());
	cJSON_AddItemToObject(obj, "Property", cJSON_Duplicate(params, 1));
	char *ptr = cJSON_Print(json);
	send_msg(CLOUD_QUEUE, ptr, strlen(ptr));
	free(ptr);
	cJSON_Delete(json);
	return 0;
}
/**********************************************************************************/

int aliyun_property_set(root_device *root, const cJSON *params, char *buf, int len)
{
	cJSON *topic = cJSON_GetObjectItemCaseSensitive(params, "Topic");
	cJSON *payload = cJSON_GetObjectItemCaseSensitive(params, "Payload");
	cJSON *proper_params = cJSON_GetObjectItemCaseSensitive(payload, "params");

	char *ptr = strdup(topic->valuestring), *optr = ptr;
	char *token = strsep(&ptr, "/"), *key = NULL;
	token = strsep(&ptr, "/");
	key = strsep(&ptr, "/");
	token = strsep(&ptr, "/");

	if (root->self_name == token) {
		gw_self_property_set(proper_params, root->alyid);
		return 0;
	}

	root->set_property_byaddr(token, proper_params);
	free(optr);
	return 0;
}

int aliyun_property_post_replay(root_device *root, const cJSON *params, char *buf, int len)
{
	cJSON *topic = cJSON_GetObjectItemCaseSensitive(params, "Topic");
	cJSON *paylod = cJSON_GetObjectItemCaseSensitive(params, "Payload");
	cJSON *code = cJSON_GetObjectItemCaseSensitive(paylod, "code");
	if (!cJSON_IsNumber(code) || code->valueint != 200) {
		return -1;
	}
	cJSON *id = cJSON_GetObjectItemCaseSensitive(paylod, "id");
	if (!cJSON_IsString(id)) {
		return -1;
	}

	char *stk = strdup(topic->valuestring);
	char *token = 0;
	if (stk) {
		token = strsep(&stk, "/");
	}
	if (stk) {
		token = strsep(&stk, "/");
	}
	if (stk) {
		token = strsep(&stk, "/");
	}
	if (stk) {
		token = strsep(&stk, "/");
	}
	if (token == NULL) {
		return -1;
	}

	cJSON *json = cJSON_CreateObject();
	cJSON_AddStringToObject(json, "Addr", token);
	cJSON_AddStringToObject(json, "Id", id->valuestring);
	root->property_post_replay(json);
	cJSON_Delete(json);

	return 0;
}

int aliyun_scenario_set(root_device *root, const cJSON *params, char *buf, int len)
{
	root->aliyun_scenario_set(params);
	ofstream os("/etc/aliynun_config/device.json", ios::trunc);
	os << *root;
	return 0;
}

int aliyun_scenario_exec(root_device *root, const cJSON *params, char *buf, int len)
{
	root->aliyun_scenario_exec(params);
	return 0;
}

int aliyun_scenario_update(root_device *root, const cJSON *params, char *buf, int len)
{
	root->aliyun_scenario_update(params);
	return 0;
}

int aliyun_scenario_remove(root_device *root, const cJSON *params, char *buf, int len)
{
	if (root->aliyun_scenario_remove(params) > 0) {
		ofstream os("/etc/aliynun_config/device.json", ios::trunc);
		os << *root;
	}
	return 0;
}

int aliyun_scenario_sync(root_device *root, const cJSON *params, char *buf, int len)
{
	return 0;
}

int aliyun_scenario_add_actions(root_device *root, const cJSON *params, char *buf, int len)
{
	if (root->aliyun_scenario_add_actions(params) > 0) {
		ofstream os("/etc/aliynun_config/device.json", ios::trunc);
		os << *root;
	}
	return 0;
}

int aliyun_scenario_update_baseInfo(root_device *root, const cJSON *params, char *buf, int len)
{
	if (root->aliyun_scenario_update_baseInfo(params) > 0) {
		ofstream os("/etc/aliynun_config/device.json", ios::trunc);
		os << *root;
	}
	return 0;
}

int aliyun_scenario_remove_actions(root_device *root, const cJSON *params, char *buf, int len)
{
	if (root->aliyun_scenario_remove_actions(params) > 0) {
		ofstream os("/etc/aliynun_config/device.json", ios::trunc);
		os << *root;
	}
	return 0;
}

int aliyun_scenario_update_actions(root_device *root, const cJSON *params, char *buf, int len)
{
	if (root->aliyun_scenario_update_actions(params) > 0) {
		ofstream os("/etc/aliynun_config/device.json", ios::trunc);
		os << *root;
	}
	return 0;
}

int aliyun_scenario_get_all(root_device *root, const cJSON *params, char *buf, int len)
{
	root->aliyun_scenario_get_all(buf, len);
	send_msg(SERVICE_QUEUE_RSP, buf, strlen(buf));
	return 0;
}

int aliyun_act_replay(const char *type, const int code = 0, const char *msg = NULL)
{
	cJSON *act_replay = cJSON_CreateObject();
	cJSON_AddStringToObject(act_replay, "Active", "ACT_REPLAY");

	cJSON *json = cJSON_AddObjectToObject(act_replay, "Params");
	cJSON_AddStringToObject(json, "type", type);
	cJSON_AddStringToObject(json, "contentType", "application/json");
	cJSON_AddNumberToObject(json, "contentLengh", 0);
	cJSON *bd = cJSON_AddObjectToObject(json, "body");
	cJSON_AddNumberToObject(bd, "code", code);
	if (msg) {
		cJSON_AddStringToObject(bd, "message", msg);
	} else {
		cJSON_AddStringToObject(bd, "message", "success");
	}
	cJSON *data = cJSON_AddObjectToObject(bd, "data");

	char *ptr = cJSON_Print(act_replay);
	send_msg(CLOUD_QUEUE, ptr, strlen(ptr));
	free(ptr);
	cJSON_Delete(act_replay);
	return 0;
}

int aliyun_gw_reset(root_device *root, const cJSON *params, char *buf, int len)
{
	if (root->mp_scene.size()) {
		root->mp_scene.clear();
		root->mp_ruleId_sceneId.clear();
		root->unused_id.clear();
		root->mp_scene_actions.clear();
		root->mp_scene_conditions.clear();
		root->mp_scene_preconditions.clear();
		ofstream os("/etc/aliynun_config/device.json", ios::trunc);
		os << *root;
	}
	aliyun_act_replay("gateWayDeviceReset");
	return 0;
}

int aliyun_gw_reboot(root_device *root, const cJSON *params, char *buf, int len)
{
	aliyun_act_replay("gateWayDeviceReboot");
	return 0;
}

int aliyun_subdev_reset(root_device *root, const cJSON *params, char *buf, int len)
{
	aliyun_act_replay("subDeviceReset");
	return 0;
}

int aliyun_subdev_reboot(root_device *root, const cJSON *params, char *buf, int len)
{
	aliyun_act_replay("subDeviceReboot");
	return 0;
}
/****************************************************************************************/
int topo_add_replay(root_device *root, const cJSON *params, char *buf, int len)
{
	cJSON *devs = cJSON_GetObjectItemCaseSensitive(params, "data");
	root->topo_add_replay(devs);
	return 0;
}

int topo_delete_replay(root_device *root, const cJSON *params, char *buf, int len)
{
	return 0;
}

int topo_get_replay(root_device *root, const cJSON *params, char *buf, int len)
{
	cJSON *devs = cJSON_GetObjectItemCaseSensitive(params, "data");
	if (root->topo_get_replay(devs)) {
		ofstream os("/etc/aliynun_config/device.json", ios::trunc);
		os << *root;
	}
	return 0;
}

int batch_login_replay(root_device *root, const cJSON *params, char *buf, int len)
{
	root->subdev_login_replay(params);
	return 0;
}

int batch_logout_replay(root_device *root, const cJSON *params, char *buf, int len)
{
	return 0;
}

int sub_register_replay(root_device *root, const cJSON *params, char *buf, int len)
{
	cJSON *devs = cJSON_GetObjectItemCaseSensitive(params, "data");
	if (root->sub_register_replay(devs)) {
		ofstream os("/etc/aliynun_config/device.json", ios::trunc);
		os << *root;
	}
	return 0;
}

int analysis_scan_online__result(root_device *root, const cJSON *params, char *buf, int len)
{
	if (root->analysis_scan_online_result(params)) {
		ofstream os("/etc/ynun_config/device.json", ios::trunc);
		os << *root;
	}
	return 0;
}

int analysis_scan_offline__result(root_device *root, const cJSON *params, char *buf, int len)
{
	root->analysis_scan_offline_result(params);
	return 0;
}

int set_dev_area(root_device *root, const cJSON *params, char *buf, int len)
{
	if (root->set_dev_area(params) > 0) {
		ofstream os("/etc/aliynun_config/device.json", ios::trunc);
		os << *root;
	}

	return 0;
}

int set_dev_scenario(root_device *root, const cJSON *params, char *buf, int len)
{
	if (root->set_dev_scenario(params) > 0) {
		ofstream os("/etc/aliynun_config/device.json", ios::trunc);
		os << *root;
	}

	return 0;
}

int product_register_replay(root_device *root, const cJSON *params, char *buf, int len)
{
	return 0;
}

int topo_change_NOTIFY(root_device *root, const cJSON *params, char *buf, int len)
{
	if (root->topo_change_notify(params) > 0) {
		ofstream os("/etc/aliynun_config/device.json", ios::trunc);
		os << *root;
	}
	return 0;
}

map<string, int (*)(root_device *root, const cJSON *params, char *buf, int len)> ctrl_func{

	/*
	*/
	{ "SET_PROPERTY_BY_ADDR", set_property_byaddr },
	{ "SET_PROPERTY_BY_AREA", set_property_byarea },
	{ "SET_PROPERTY_BY_SCENARIO", set_property_bysaio },

	{ "GET_ALL_DEVSTATUS", get_all_devstatus },
	{ "GET_STATUS_BY_ADDR", get_status_byaddr },
	{ "GET_STATUS_BY_AREA", get_status_byarea },
	{ "GET_STATUS_BY_SCENARIO", get_status_bysaio },

	{ "GET_ALL_DEV", get_all_dev },
	{ "GET_DEV_BY_ADDR", get_dev_byaddr },
	{ "GET_DEV_BY_AREA", get_dev_byarea },
	{ "GET_DEV_BY_SCENARIO", get_dev_bysaio },

	{ "SET_DEV_AREA", set_dev_area },
	{ "SET_DEV_SCENARIO", set_dev_scenario },
	{ "GET_ALL_SCENE", aliyun_scenario_get_all },

	/*
	aliyun
	*/
	{ "gateWayDeviceReboot", aliyun_gw_reboot },
	{ "gateWayDeviceReset", aliyun_gw_reset },
	{ "subDeviceReboot", aliyun_subdev_reboot },
	{ "subDeviceReset", aliyun_subdev_reset },
	/*
	scene
	*/
	{ "issueScene", aliyun_scenario_set },
	{ "execScene", aliyun_scenario_exec },
	{ "updateScene", aliyun_scenario_update },
	{ "removeScene", aliyun_scenario_remove },
	{ "syncScene", aliyun_scenario_sync },
	{ "addSceneActions", aliyun_scenario_add_actions },
	{ "removeSceneActions", aliyun_scenario_remove_actions },
	{ "updateSceneActions", aliyun_scenario_update_actions },
	{ "updateSceneBaseInfo", aliyun_scenario_update_baseInfo },
	{ "getAllScene", aliyun_scenario_get_all },

	/*
	scan_result
	*/
	{ "SCAN_ONLINE_RESULT", analysis_scan_online__result },
	{ "SCAN_OFFLINE_RESULT", analysis_scan_offline__result },
	/*
	cloud msg
	*/
	{ "CLOUD_ONLINE_EVENT", cloud_online_event },
	{ "CLOUD_OFFLINE_EVENT", cloud_offline_event },
	/*
	aliyun master  msg
	*/
	{ "thing.service.property.set", aliyun_property_set },
	{ "thing.event.property.post", aliyun_property_post_replay },
	/*
	aliyun subdev reply msg
	*/
	{ "AIOT_SUBDEVRECV_TOPO_ADD_REPLY", topo_add_replay },
	{ "AIOT_SUBDEVRECV_TOPO_DELETE_REPLY", topo_delete_replay },
	{ "AIOT_SUBDEVRECV_TOPO_GET_REPLY", topo_get_replay },
	{ "AIOT_SUBDEVRECV_BATCH_LOGIN_REPLY", batch_login_replay },
	{ "AIOT_SUBDEVRECV_BATCH_LOGOUT_REPLY", batch_logout_replay },
	{ "AIOT_SUBDEVRECV_SUB_REGISTER_REPLY", sub_register_replay },
	{ "AIOT_SUBDEVRECV_PRODUCT_REGISTER_REPLY", product_register_replay },
	{ "AIOT_SUBDEVRECV_TOPO_CHANGE_NOTIFY", topo_change_NOTIFY },

};

void ctrl_msg_process(root_device *root, const char *msg, char *buf = NULL, int len = 0)
{
	cJSON *json = cJSON_Parse(msg);
	cJSON *act = cJSON_GetObjectItemCaseSensitive(json, "Active");
	cJSON *params = cJSON_GetObjectItemCaseSensitive(json, "Params");
	if (cJSON_IsString(act) && act->valuestring != NULL) {
		if (ctrl_func[act->valuestring]) {
			ctrl_func[act->valuestring](root, params, buf, len);
			return cJSON_Delete(json);
		}
	}

	cJSON *topic = cJSON_GetObjectItemCaseSensitive(json, "Topic");
	cJSON *payload = cJSON_GetObjectItemCaseSensitive(json, "Payload");
	cJSON *method = cJSON_GetObjectItemCaseSensitive(payload, "method");
	cJSON *type = cJSON_GetObjectItemCaseSensitive(payload, "type");
	if (cJSON_IsString(method) && method->valuestring != NULL) {
		if (ctrl_func[method->valuestring]) {
			ctrl_func[method->valuestring](root, json, buf, len);
			return cJSON_Delete(json);
		}
	}
	if (cJSON_IsString(type) && type->valuestring != NULL) {
		if (ctrl_func[type->valuestring]) {
			cJSON *body = cJSON_GetObjectItemCaseSensitive(payload, "body");
			ctrl_func[type->valuestring](root, body, buf, len);
			return cJSON_Delete(json);
		}
	}

	cJSON *replay = cJSON_GetObjectItemCaseSensitive(json, "Reply");
	params = cJSON_GetObjectItemCaseSensitive(json, "Params");
	if (cJSON_IsString(replay) && replay->valuestring) {
		if (ctrl_func[replay->valuestring]) {
			ctrl_func[replay->valuestring](root, params, buf, len);
			return cJSON_Delete(json);
		}
	}
	cJSON_Delete(json);
}
/*******************************************************************************/
int ctrl_online()
{
	cJSON *json = cJSON_CreateObject();
	cJSON_AddStringToObject(json, "Active", "CTRL_ONLINE_EVENT");
	char *ptr = cJSON_Print(json);
	send_msg(CLOUD_QUEUE, ptr, strlen(ptr), 0, 1);
	free(ptr);
	cJSON_Delete(json);
	return 0;
}

int main()
{
	root_device root;
	char msg[1024 * 8], buf[1024 * 8];
	while (recv_msg(CTRL_QUEUE, buf, sizeof(buf), O_NONBLOCK) > 0) {
		printf("discard msg:%s\n", buf);
	}
	root.start();
	sleep(1);
	ctrl_online();
	for (;;) {
		if (recv_msg(CTRL_QUEUE, msg, sizeof(msg)) > 0) {
			printf("recv msg:%s\n", msg);
			ctrl_msg_process(&root, msg, buf, sizeof(buf));
			printf("proces msg finished\n");
		} else {
			perror("recv_msg");
		}
	}
	return 0;
}